package org.datascooter.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.datascooter.DataScooter;
import org.datascooter.bundle.DSMapper;
import org.datascooter.bundle.EntityBundle;
import org.datascooter.bundle.attribute.AbstractAttribute;
import org.datascooter.bundle.attribute.ContainerAttribute;
import org.datascooter.bundle.attribute.PersistAttribute;
import org.datascooter.bundle.attribute.PersistReferenceAttribute;
import org.datascooter.bundle.attribute.Reference;
import org.datascooter.exception.DataScooterException;
import org.datascooter.inface.IDataManager;

/**
 * Utility for invoking reference methods from objects
 * 
 * @author nemo
 * 
 */
public class ReferenceInvoker {

	public static <T> T invoke(Object obj, String methodName) {
		return invokeContext(obj, methodName, DataScooter.getDefault());
	}

	@SuppressWarnings("rawtypes")
	public static Collection invokeCollection(Object obj, String methodName) {
		return invokeCollectionContext(obj, methodName, DataScooter.getDefault());
	}

	@SuppressWarnings("rawtypes")
	public static Map invokeMap(Object obj, String methodName) {
		return invokeMapContext(obj, methodName, DataScooter.getDefault());
	}

	@SuppressWarnings("unchecked")
	public static <T> T invokeContext(Object obj, String methodName, IDataManager manager) {
		try {
			EntityBundle bundle = DSMapper.getBundle(obj.getClass().getName());
			AbstractAttribute attribute = bundle.getAttributeReference(methodName + "Id");
			if (attribute != null) {
				Object executeGetter = ((PersistReferenceAttribute) attribute).getValue(obj);
				if (executeGetter != null) {
					return manager.firstById(((PersistReferenceAttribute) attribute).getEntity(), executeGetter);
				}
			} else {
				attribute = bundle.getAttributeReference(LangUtils.prepareName(methodName) + "Id");
				if (attribute != null) {
					Object executeGetter = ((PersistReferenceAttribute) attribute).getValue(obj);
					if (executeGetter != null) {
						return manager.firstById(((PersistReferenceAttribute) attribute).getEntity(), executeGetter);
					}
				}
			}
			attribute = bundle.getAttributeReference(methodName);
			if (attribute != null) {
				Reference reference = (Reference) attribute;
				reference.hookValue(obj);
				return manager.first(reference.getDataSnip().casheSnip(false));
			}
			if (bundle.hasContainers) {
				String attrName = LangUtils.prepareName(methodName);
				ContainerAttribute containerAttribute = bundle.getContainerAttribute(attrName);
				if (containerAttribute != null) {
					List<Object> content = manager.getInstanceManager().getContent(obj, attrName);
					if (content.size() > 0) {
						return (T) content.get(0);
					}
				}
			}
		} catch (Exception e) {
			throw new DataScooterException(e);
		}
		return null;
	}

	@SuppressWarnings({
			"unchecked", "rawtypes"
	})
	public static Collection invokeCollectionContext(Object obj, String methodName, IDataManager manager) {
		try {
			EntityBundle bundle = DSMapper.getBundle(obj.getClass().getName());
			AbstractAttribute attribute = bundle.getAttributeReference(methodName);
			if (attribute != null) {
				Reference reference = (Reference) attribute;
				reference.hookValue(obj);
				switch (reference.getType()) {
					case SET:
						Set emptySet = new HashSet();
						emptySet.addAll(manager.list(reference.getDataSnip().casheSnip(false)));
						return emptySet;
					case LIST:
						return manager.list(reference.getDataSnip().casheSnip(false));
				}
			}
			if (bundle.hasContainers) {
				String attrName = LangUtils.prepareName(methodName);
				ContainerAttribute containerAttribute = bundle.getContainerAttribute(attrName);
				if (containerAttribute != null) {
					return manager.getInstanceManager().getContent(obj, attrName);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new DataScooterException(e);
		}
		return new ArrayList();
	}

	@SuppressWarnings({
			"unchecked", "rawtypes"
	})
	public static Map invokeMapContext(Object obj, String methodName, IDataManager manager) {
		try {
			EntityBundle bundle = DSMapper.getBundle(obj.getClass().getName());
			AbstractAttribute attribute = bundle.getAttributeReference(methodName);
			if ((attribute != null) && (attribute instanceof Reference)) {
				Reference reference = (Reference) attribute;
				reference.hookValue(obj);
				if (reference != null) {
					switch (reference.getType()) {
						case MAP:
							Map emptyMap = new HashMap();
							List<Object> list = manager.list(reference.getDataSnip().casheSnip(false));
							if (list.size() > 0) {
								Object object2 = list.get(0);
								EntityBundle bundle2 = DSMapper.getBundle(object2.getClass().getName());
								PersistAttribute attribute2 = bundle2.getAttribute(reference.getKeyAttribute());
								for (Object object : list) {
									emptyMap.put(attribute2.getValue(object), object);
								}
							}
							return emptyMap;
					}
				}
			}
			if (bundle.hasContainers) {
				String attrName = LangUtils.prepareName(methodName);
				ContainerAttribute containerAttribute = bundle.getContainerAttribute(attrName);
				if (containerAttribute != null) {
					List<Object> content = manager.getInstanceManager().getContent(obj, attrName);
					if (content.size() > 0) {
						Map emptyMap = new HashMap();
						bundle = null;
						PersistAttribute keyAttribute = null;
						for (int a = 0; a < content.size(); a++) {
							if (bundle == null) {
								bundle = DSMapper.getBundle(content.get(0).getClass().getName());
							}
							keyAttribute = bundle.getAttribute(containerAttribute.getMapKeyAttribute());
							emptyMap.put(keyAttribute.getValue(content.get(a)), content.get(a));
						}
						return emptyMap;
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new DataScooterException(e);
		}
		return new HashMap();
	}
}
